package tests;

import static org.junit.Assert.*;

import java.util.LinkedList;
import java.util.Random;

import org.junit.BeforeClass;
import org.junit.Test;

import Exceptions.InvalidAgeParam;
import Exceptions.InvalidChildID;
import Exceptions.InvalidHeightParam;
import Exceptions.TicketDoesntExist;
import sourceFiles.ChildEntry;

/**
 * This test class is a unit test for the class ChildEntry
 * 
 * @author LASO-Team
 * 
 */
public class ChildEntryTest {

	// static parameters used in the test a defined here
	static private final int RANDOM_TEST_SIZE = 7;
	static private final int[] EDGE_OF_LEGAL_INT_VALUES = { 1, 2 };
	static private final int[] EDGE_OF_ILLEGAL_INT_VALUES = { -1, 0 };
	static private final int TEST_SIZE = RANDOM_TEST_SIZE
			+ EDGE_OF_LEGAL_INT_VALUES.length;
	static private final int NUM_TICKETS_PER_RIDE = 10;
	static private final int MAX_RANDOM = Integer.MAX_VALUE;

	static private int[] legalChildIDs;
	static private int[] legalChildAges;
	static private int[] legalChildHeights;
	static private int[] legalRideIDs;
	static private int[] illegalChildIDs;
	static private int[] illegalChildAges;
	static private int[] illegalChildHeights;
	static private int[] illegalRideIDs;
	static private int noRealID = 1111111;
	// end static parameters
	
	/**
	 * Initiating all the static arrays above with test values, as well as
	 * random parameters (number of tests, TEST_SIZE, is determined by the
	 * number of test values as well as the number of random values
	 * 
	 * @throws Exception
	 */
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
		assertTrue(EDGE_OF_LEGAL_INT_VALUES.length == EDGE_OF_ILLEGAL_INT_VALUES.length);

		LinkedList<Integer> legalChildIDsList = new LinkedList<Integer>();
		LinkedList<Integer> legalChildAgesList = new LinkedList<Integer>();
		LinkedList<Integer> legalChildHeightsList = new LinkedList<Integer>();
		LinkedList<Integer> legalRideIDsList = new LinkedList<Integer>();
		LinkedList<Integer> illegalChildIDsList = new LinkedList<Integer>();
		LinkedList<Integer> illegalChildAgesList = new LinkedList<Integer>();
		LinkedList<Integer> illegalChildHeightsList = new LinkedList<Integer>();
		LinkedList<Integer> illegalRideIDsList = new LinkedList<Integer>();

		int numOfEdgeTests = EDGE_OF_LEGAL_INT_VALUES.length;
		for (int i = 0; i < numOfEdgeTests; i++) { // init the legal value first
			legalChildIDsList.add(EDGE_OF_LEGAL_INT_VALUES[i]);
			legalChildAgesList.add(EDGE_OF_LEGAL_INT_VALUES[i]);
			legalChildHeightsList.add(EDGE_OF_LEGAL_INT_VALUES[i]);
			legalRideIDsList.add(EDGE_OF_LEGAL_INT_VALUES[i]);
			illegalChildIDsList.add(EDGE_OF_ILLEGAL_INT_VALUES[i]);
			illegalChildAgesList.add(EDGE_OF_ILLEGAL_INT_VALUES[i]);
			illegalChildHeightsList.add(EDGE_OF_ILLEGAL_INT_VALUES[i]);
			illegalRideIDsList.add(EDGE_OF_ILLEGAL_INT_VALUES[i]);
		}
		Random rand = new Random();
		int newRandID;
		for (int i = 0; i < RANDOM_TEST_SIZE; i++) {
			newRandID = rand.nextInt(MAX_RANDOM) + 1;
			while (legalChildIDsList.contains(newRandID)) {
				newRandID = rand.nextInt(MAX_RANDOM) + 1;
			}
			legalChildIDsList.add(newRandID);

			newRandID = rand.nextInt(MAX_RANDOM) + 1;
			while (legalRideIDsList.contains(newRandID)) {
				newRandID = rand.nextInt(MAX_RANDOM) + 1;
			}
			legalRideIDsList.add(newRandID);

			legalChildAgesList.add(rand.nextInt(MAX_RANDOM) + 1);
			legalChildHeightsList.add(rand.nextInt(MAX_RANDOM) + 1);
			illegalChildIDsList.add(-rand.nextInt(Integer.MAX_VALUE));
			illegalChildAgesList.add(-rand.nextInt(Integer.MAX_VALUE));
			illegalChildHeightsList.add(-rand.nextInt(Integer.MAX_VALUE));
			illegalRideIDsList.add(-rand.nextInt(Integer.MAX_VALUE));
		}
		assertTrue(legalChildIDsList.size() == TEST_SIZE);
		assertTrue(legalRideIDsList.size() == TEST_SIZE);

		legalChildIDs = new int[TEST_SIZE];
		legalChildAges = new int[TEST_SIZE];
		legalChildHeights = new int[TEST_SIZE];
		legalRideIDs = new int[TEST_SIZE];
		illegalChildIDs = new int[TEST_SIZE];
		illegalChildAges = new int[TEST_SIZE];
		illegalChildHeights = new int[TEST_SIZE];
		illegalRideIDs = new int[TEST_SIZE];
		for (int i = 0; i < TEST_SIZE; i++) {
			legalChildIDs[i] = legalChildIDsList.get(i);
			legalChildAges[i] = legalChildAgesList.get(i);
			legalChildHeights[i] = legalChildHeightsList.get(i);
			legalRideIDs[i] = legalRideIDsList.get(i);
			illegalChildIDs[i] = illegalChildIDsList.get(i);
			illegalChildAges[i] = illegalChildAgesList.get(i);
			illegalChildHeights[i] = illegalChildHeightsList.get(i);
			illegalRideIDs[i] = illegalRideIDsList.get(i);
		}
		return;
	}

	/**
	 * Tests that we can create the class properly.
	 * 
	 * @throws InvalidHeightParam
	 *             if the height is invalid.
	 * @throws InvalidAgeParam
	 *             if the age is invalid.
	 * @throws InvalidChildID
	 *             if the child id is invalid.
	 */
	@Test
	public void testChildEntryCtor() throws InvalidAgeParam,
			InvalidHeightParam, InvalidChildID {
		ChildEntry child;
		for (int i = 0; i < TEST_SIZE; i++) {
			try {
				child = new ChildEntry(illegalChildIDs[i], legalChildAges[i],
						legalChildHeights[i]);
				fail("tried to create a child with an illegal child ID = "
						+ illegalChildIDs[i]);
			} catch (InvalidChildID e) {
			}
			try {
				child = new ChildEntry(legalChildIDs[i], illegalChildAges[i],
						legalChildHeights[i]);
				fail("tried to create a child with an illegal age = "
						+ illegalChildAges[i]);
			} catch (InvalidAgeParam e) {
			}
			try {
				child = new ChildEntry(legalChildIDs[i], legalChildAges[i],
						illegalChildHeights[i]);
				fail("tried to create a child with an illegal height = "
						+ illegalChildHeights[i]);
			} catch (InvalidHeightParam e) {
			}

			child = new ChildEntry(legalChildIDs[i], legalChildAges[i],
					legalChildHeights[i]);
			assertTrue(child.getID() == legalChildIDs[i]);
			assertTrue(child.getAge() == legalChildAges[i]);
			assertTrue(child.getHeight() == legalChildHeights[i]);
		}

	}

	/**
	 * Tests that we can insert a new ticket to the tickets list.
	 * 
	 * @throws InvalidHeightParam
	 *             if the height is invalid.
	 * @throws InvalidAgeParam
	 *             if the age is invalid.
	 * @throws InvalidChildID
	 *             if the child id is invalid.
	 */
	@Test
	public void testAddTicket() throws InvalidAgeParam, InvalidHeightParam,
			InvalidChildID {
		ChildEntry child = new ChildEntry(legalChildIDs[0], legalChildAges[0],
				legalChildHeights[0]);
		testAddingRegularTicket(child);
		testAddingExtremeTicket(child);
	}

	/**
	 * Tests that we can remove a ticket from the tickets list.
	 * 
	 * @throws InvalidHeightParam
	 *             if the height is invalid.
	 * @throws InvalidAgeParam
	 *             if the age is invalid.
	 * @throws TicketDoesntExist
	 *             if the ticket doesn't exist.
	 * @throws InvalidChildID
	 *             if the child id is invalid.
	 */
	@Test
	public void testRemoveTicket() throws InvalidAgeParam, InvalidHeightParam,
			TicketDoesntExist, InvalidChildID {
		ChildEntry child = new ChildEntry(legalChildIDs[0], legalChildAges[0],
				legalChildHeights[0]);
		testRemovingRegularTicket(child);
		testRemovingExtremeTicket(child);

	}

	/**
	 * Tests the validity of the predicate ExistsTicket.
	 * 
	 * @throws InvalidHeightParam
	 *             if the height is invalid.
	 * @throws InvalidAgeParam
	 *             if the age is invalid.
	 * @throws InvalidChildID
	 *             if the child id is invalid.
	 */
	@Test
	public void testExistsTicket() throws InvalidAgeParam, InvalidHeightParam,
			InvalidChildID {
		ChildEntry child = new ChildEntry(legalChildIDs[0], legalChildAges[0],
				legalChildHeights[0]);
		for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test adding regular
															// tickets
			child.addTicket(noRealID, false);
			assertTrue(child.existsTicket(noRealID, false));
		}
		for (int i = 0; i < TEST_SIZE; i++) { // test adding extreme tickets
			assertTrue(!child.existsTicket(legalRideIDs[i], true));
			for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) {
				child.addTicket(legalRideIDs[i], true);
				assertTrue(child.existsTicket(legalRideIDs[i], true));
			}
		}
	}

	private void testAddingExtremeTicket(ChildEntry child) {
		for (int i = 0; i < TEST_SIZE; i++) { // test adding extreme tickets
			assertTrue(!child.existsTicket(legalRideIDs[i], true));
			for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) {
				child.addTicket(legalRideIDs[i], true);
				assertTrue(child.existsTicket(legalRideIDs[i], true));
				assertTrue(child.getChildsTicket().getExtremeRidesList()
						.get(legalRideIDs[i]) == (j + 1));
			}
		}
	}

	private void testAddingRegularTicket(ChildEntry child) {
		assertTrue(!child.existsTicket(noRealID, false));
		for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test adding regular
			// tickects
			child.addTicket(noRealID, false);
			assertTrue(child.existsTicket(noRealID, false));
			assertTrue(child.getChildsTicket().getRegualrRidesTickets() == (j + 1));
		}
	}

	private void testRemovingExtremeTicket(ChildEntry child)
			throws TicketDoesntExist {
		for (int i = 0; i < TEST_SIZE; i++) {

			for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test adding and
				// removing regular
				assertTrue(!child.existsTicket(legalRideIDs[i], true));
				child.addTicket(legalRideIDs[i], true);
				assertTrue(child.existsTicket(legalRideIDs[i], true));
				child.removeTicket(legalRideIDs[i], true);
				assertTrue(!child.existsTicket(legalRideIDs[i], true));
			}
			for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // adding regular
				// tickets
				child.addTicket(legalRideIDs[i], true);
			}
			for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test removing
				// regular tickets
				assertTrue(child.existsTicket(legalRideIDs[i], true));
				child.removeTicket(legalRideIDs[i], true);
			}
			assertTrue(!child.existsTicket(legalRideIDs[i], true));
		}
	}

	private void testRemovingRegularTicket(ChildEntry child)
			throws TicketDoesntExist {
		for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test adding and
															// removing regular
			assertTrue(!child.existsTicket(noRealID, false));
			child.addTicket(noRealID, false);
			assertTrue(child.existsTicket(noRealID, false));
			child.removeTicket(noRealID, false);
			assertTrue(!child.existsTicket(noRealID, false));
		}
		for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // adding regular
															// tickets
			child.addTicket(noRealID, false);
		}
		for (int j = 0; j < NUM_TICKETS_PER_RIDE; j++) { // test removing
															// regular tickets
			assertTrue(child.existsTicket(noRealID, false));
			child.removeTicket(noRealID, false);
		}
		assertTrue(!child.existsTicket(noRealID, false));
	}

}
